#!/usr/bin/lua


--------------------------------------------
---                                      ---
---    Simulador de autômatos finitos    ---
---    Versão Linux                      ---
---                                      ---
--------------------------------------------


--------------- Variáveis Globais ---------------

-- Indica se imprime informações de rastreamento
imprimeInfo = nil

-- Indica se a cadeia será lida do teclado
leTeclado = nil

-- Arquivo que contém a especificação
fileSpecs = nil

-- Arquivo que contém a cadeia
fileCadeia = nil

-- Tabela de estados e transições
transicoes = {}

-- Tabela que identifica os estados de aceitação
aceitacao = {}

-- Variável que contém o número de estados do autômato
numeroEstados = nil

-- Variável que contém o estado inicial do autômato
estadoInicial = nil

-- Variável que contém o estado atual do autômato
estadoAtual = nil

-- Variável que contém o próximo estado da transição
estadoProximo = nil


--------------- Fim de Variáveis Globais ---------------


--------------- Funções ---------------

-- Função que adiciona o estado e suas transições à tabela de estados e transições
function adicionaEstado (baseTransicao)

	if imprimeInfo == true then
		print ("Estado: ", baseTransicao)

		print ("Transições a partir deste estado:")

	end

	-- Cria uma entrada na tabela para o estado que está sendo lido do arquivo
	transicoes[baseTransicao] = {}

	-- Marca, neste primeiro momento, o estado como não sendo de aceitação
	aceitacao[baseTransicao] = false

	-- Cria uma tabela auxiliar com as transições do estado lido
	transicaoAux = {}

	charCount = 0

	-- Identifica no arquivo os símbolos
	for char in string.gmatch (currentLine, " . ") do
		char2Count = 0

		-- Loop para corrigir aquisição do símbolo
		-- Símbolo é adquirido como " K " ao invés de "K"
		for char2 in string.gmatch (char, ".") do
			char2Count = char2Count + 1

			if char2Count == 2 then
				char = char2

				break

			end

		end

		estadoCount = 0

		charCount = charCount + 1

		transicaoAux[char] = {}

		-- Identifica no arquivo o estado após a transição
		for estadoFinal in string.gmatch (currentLine, "(%w+);") do
			estadoCount = estadoCount + 1
	
			if (estadoCount == charCount) then
				transicaoAux[char] = estadoFinal

				if imprimeInfo == true then
					print (baseTransicao, char, transicaoAux[char])

				end

			end

		end

	end


	-- Inclui os símbolos e transições para o estado na tabela
	transicoes[baseTransicao] = transicaoAux

end


-- Função que verifica se o símbolo pertence ao alfabeto
function verificaSimbolo (simbolo)

	for char in string.gmatch (alfabeto, simbolo) do
		flag = true

		if flag == true then
			return true

		end

	end

end


-- Função que lê o símbolo e retorna o próximo estado
function transicaoEstado (simbolo)

	-- Próximo estado
	estadoProximo = transicoes[estadoAtual][simbolo]

	if estadoProximo == nil then
		if imprimeInfo == true then
			print (estadoAtual, simbolo)
			
			print ("Transição inexistente")

		end
		
	else
		if imprimeInfo == true then
			print (estadoAtual, simbolo, estadoProximo)
		end

	end

	return estadoProximo

end


--------------- Fim de Funções ---------------


--------------- Main ---------------

print ("\n")

print ("Deseja imprimir informações para rastreamento? Digite 's' para sim ou 'n' para não e pressione Enter: ")

imprimeInfoAux = io.read ()

-- Troca o tipo do valor da variável de string para booleano
if imprimeInfoAux == "s" then
	imprimeInfo = true

else
	imprimeInfo = false

end


---------- Leitura do arquivo de especificação do autômato ----------

while fileSpecs == nil do

	print ("\n")

	print ("Digite o nome do arquivo que contém a especificação do autômato e pressione Enter.")

	print ("Se nenhum nome for especificado, será utilizado o arquivo 'automato.txt'")

	filename = io.read ()

	if filename == "" then
		fileSpecs = io.open ("automato.txt", "r")

	else
		fileSpecs = io.open (filename, "r")	

	end


	if fileSpecs == nil then
		print ("Arquivo não encontrado!")

		break

	end

end


----- Alfabeto -----

-- Lê do arquivo de entrada o alfabeto

alfabeto = fileSpecs:read ("*l")

if imprimeInfo == true then
	print ("\n")

	print ("Descrição do autômato")

	print ("\n")

	print ("Alfabeto: ", alfabeto)

end


----- Número de estados -----

-- Lê do arquivo de entrada o número de estados do autômato

numeroEstados = fileSpecs:read ("*l")

if imprimeInfo == true then
	print ("Número de estados: ", numeroEstados)

end


----- Estado -----

-- Adiciona os estados e transições do arquivo na tabela de transição de estados

for k = 1, numeroEstados do
	currentLine = fileSpecs:read ("*l")

	if imprimeInfo == true then
		print ("\n")

	end

	for estado in string.gmatch (currentLine, "(%w+):") do
		adicionaEstado (estado)

	end

end


----- Estado Inicial -----

-- Extrai do arquivo o estado inicial do autômato

estadoInicial = fileSpecs:read ("*l")

if imprimeInfo == true then
	print ("\n")

	print ("Estado inicial: ", estadoInicial)

end


----- Estados de aceitação -----

-- Extrai do arquivo os estados de aceitação do autômato

currentLine = fileSpecs:read ("*l")

if imprimeInfo == true then
	print ("Estados de aceitação: ", currentLine)

end

for estado in string.gmatch (currentLine, "(%w+)") do
	aceitacao[estado] = true

end


---------- Fim de Leitura do arquivo de especificação do autômato ----------


---------- Leitura do arquivo que contém a cadeia de entrada ----------

while leTeclado == nil do

	print ("\n")

	print ("Deseja ler a cadeia a partir de um arquivo ou do teclado?")

	print ("Digite 'a' para ler de arquivo ou 't' para ler do teclado e pressione Enter")

	leTecladoAux = io.read ()

	-- Troca o tipo do valor da variável de string para booleano
	if leTecladoAux == "t" then
		leTeclado = true

	elseif leTecladoAux == "a" then
		leTeclado = false

	end


	if leTeclado == false then

		print ("\n")

		print ("Digite o nome do arquivo que contém a cadeia de entrada e pressione Enter.")

		print ("Se nenhum nome for especificado, será utilizado o arquivo 'cadeia.txt'")

		filename = io.read ()

		if filename == "" then
			fileCadeia = io.open ("cadeia.txt", "r")

		else
			fileCadeia = io.open (filename, "r")	

		end

		if fileCadeia == nil then
			print ("Arquivo não encontrado!")

			break

		end

	end

end


---------- Fim de Leitura do arquivo que contém a cadeia de entrada ----------


---------- Transição de estados ----------

-- Lê do arquivo ou teclado a cadeia de entrada
if leTeclado == false then
	cadeia = fileCadeia:read ("*l")

elseif leTeclado == true then
	print ("\n")

	print ("Digite a cadeia de entrada e pressione Enter: ")

	cadeia = io.read ()

end


-- Posiciona o autômato em seu estado inicial
estadoAtual = estadoInicial

if imprimeInfo == true then
	print ("\n")

	print ("Cadeia: ", cadeia)

	print ("\n")

	print ("Estado inicial: ", estadoAtual)

	print ("\n")

	print ("Transições:")

end


-- Executa a transição de estados para cada símbolo da cadeia
for simbolo in string.gmatch (cadeia, ".") do
	existeSimbolo = nil

	-- Verifica se o símbolo pertence ao alfabeto e armazena em uma variável booleana
	existeSimbolo = verificaSimbolo (simbolo)
		

	-- Sai do loop caso o símbolo não pertença ao alfabeto
	if existeSimbolo ~= true then
		if imprimeInfo == true then
			print (estadoAtual, simbolo)
			print ("Símbolo inexistente no alfabeto: ", simbolo)

		end

		estadoAtual = nil

		break

	-- Efetua a transição de estados caso o símbolo pertença ao alfabeto
	elseif existeSimbolo == true then
		estadoAtual = transicaoEstado (simbolo)

	end
	
	if estadoAtual == nil then
		break

	end

end


print ("\n")


-- Verifica se o estado é de aceitação
if aceitacao[estadoAtual] == true then
	print ("Cadeia aceita")

elseif aceitacao[estadoAtual] == false and estadoProximo ~= nil then
	print ("Estado não é de aceitação")

	print ("Cadeia rejeitada")

else
	print ("Transição e/ou símbolo inexistente(s)")

	print ("Cadeia rejeitada")

end

print ("\n")


---------- Fim de Transição de estados ----------


print ("Pressione Enter para encerrar")

encerra = io.read ()


--------------- Fim de Main ---------------


--------------- Fim do arquivo ---------------

